<template>
  <div ref="topoRef" id="topo" style="flex-grow: 1; overflow:'hidden';height: calc(100vh - 210px); "></div>
  <TopoNodeDetail :visible="nodeDrawerState.visible" :srcNodes="nodeDrawerState.srcNodes"
    :defaultActiveKey="nodeDrawerState.defaultKey" apiType="topology" :nodeInfo="nodeToCheck"
    @on-close="closeNodeDetail" />
  <TopoEdgeDetail :visible="edgeDrawerState.visible" :edgeInfo="edgeToCheck" drawerType="workload"
    @on-close="closeEdgeDetail" />
</template>
<script>
export default {
  name: "TopologyMain"
}
</script>
<script setup>
import { ref, reactive, watch, onMounted, onUnmounted, toRef } from 'vue'
import { forEach, cloneDeep, minBy, maxBy, map, uniqBy } from 'lodash'

import TopoNodeDetail from '../topoNodeDetail'
import TopoEdgeDetail from '../topoEdgeDetail'
import { basicTopoInteractions } from './topoInteraction';
import { filterByFieldAndCount, getNodeAndEdgeStatus } from './topoService'
import { nameTooltip } from './graphPlugins';
import './customNode'
import './customLayout'
import './customCombo'
import * as G6 from '@antv/g6'
const props = defineProps({
  queryTime: {
    type: Object
  },
  layout: String,
  topoData: Object,
  isFullScreen: Boolean,
  nodeMode: String,
  dataFilter: {
    type: Object,
    default: () => ({ count: 20, field: 'responseTime' })
  }
})
const emits = defineEmits(['update-entrance-node'])
const topoRef = ref()
const graph = ref()
const edgeRequestTotal = reactive({ min: 0, max: 0 })
const nodeToCheck = ref()
const nodeDrawerState = ref({
  visible: false,
  srcNodes: [],
  defaultKey: ''
})
const edgeToCheck = ref()
const edgeDrawerState = ref({
  visible: false,
})
// 当监听到window对象或者container的大小发生变化是，改变画布大小
const changeGraphSize = () => {
  if (graph.value && topoRef.value) {
    const { clientWidth, clientHeight } = topoRef.value;
    graph.value.changeSize(clientWidth, clientHeight);
    // this.graph.fitCenter()
  }
};

// 设置线上最大最小请求量数据
const setEdgeRequestTotalRange = () => {
  const { topoData } = props;
  edgeRequestTotal.min =
    minBy(topoData.edges, "requestTotal")?.requestTotal || 0;
  edgeRequestTotal.max =
    maxBy(topoData.edges, "requestTotal")?.requestTotal || 0;
};
const getLayoutConfig = () => {
  const { layout } = props;
  console.log(layout, 'layout');

  if (layout === "namespace") {
    return {
      type: "comboCombined",
      spacing: 20,
      nodeSize: 180,
      comboPadding: props.nodeMode === 'circle' ? 100 : 10,
      // preventOverlap: true,
      // workerEnabled: true,
      outerLayout: new G6.Layout["gForce"]({
        // gravity:16,
        factor: 4,
        // * 164 * 70
        nodeSize: [164, 70],
        // preventOverlap: true,
        edgeStrength: 100,
        nodeSpacing: 80,
        linkDistance: 700,
        getMass(d) {
          // console.log(d, 'd', d.itemType);
          return d.hasConnect ? 20 : 12;
        }
      }),

      innerLayout: new G6.Layout['grid']({
        // ... // 该布局的参数
        // size: [221, 123]
        nodeSize: props.nodeMode === 'circle' ? [80, 80] : [240, 140]
      })
    };
  } else if (layout === "merge") {
    return {
      type: "custom-merge-layout",
      // type: 'custom-layout',
    };
  } else {
    return {

    };
  }
};

const setEdgeLineWidth = (graph) => {
  graph.setAutoPaint(false);
  const edges = graph.getEdges();
  const segment =
    (edgeRequestTotal.max - edgeRequestTotal.min) / 5;
  forEach(edges, (edge) => {
    const edgeModel = edge.getModel();
    const edgeStatus = getNodeAndEdgeStatus(edgeModel);
    const { requestTotal } = edgeModel;
    const lineWidth = segment
      ? Math.ceil((requestTotal - edgeRequestTotal.min) / segment)
      : 1;

    edgeModel.style.stroke = edgeStatus ? "#F7313B" : "#AAAAAA";
    edgeModel.style.lineWidth = lineWidth || 1;
    edgeModel.style.endArrow = {
      path: G6.Arrow.triangle(4 + lineWidth, 4 + lineWidth, 0),
      fill: edgeStatus ? "#F7313B" : "#AAAAAA",
    };
    edgeModel.style.cursor = "pointer";

    graph.updateItem(edge, edgeModel);
  });
  graph.paint();
  graph.setAutoPaint(true);
};
const draw = () => {
  const { topoData, layout, updateIsEntranceNode, isAddExternal } =
    props;

  if (!topoData.nodes.length) {
    topoRef.value.innerHTML = "";
    return;
  }
  const { count, field } = props.dataFilter
  console.log(topoData, 'topoData');

  const { nodes, edges, combos } = filterByFieldAndCount(cloneDeep(topoData), field, count);
  const data = { nodes, edges, combos };
  console.log(nodes, 'nodes', edges, 'edges');

  //G6 配置：平行边以贝塞尔曲线分离
  G6.Util.processParallelEdges(
    data.edges,
    15,
    "quadratic",
    undefined,
    "loop"
  );

  const width = topoRef.value.clientWidth;
  const height = topoRef.value.clientHeight;
  topoRef.value.innerHTML = "";

  const contextMenu = new G6.Menu({
    getContent(evt) {
      const node = evt.item.getModel();
      return `<ul class='context_menu_list'>
                    ${node.isEntrance
          ? `<li data-code='removeEntrance'>取消设置入口节点</li>`
          : `<li data-code='addEntrance'>设置入口节点</li>`
        }
                </ul>`;
    },
    handleMenuClick: (target, item) => {
      const code = target.getAttribute("data-code");
      const node = item.getModel();
      if (code === "removeEntrance") {
        emits('update-entrance-node', node, 0);
      }
      if (code === "addEntrance") {
        emits('update-entrance-node', node, 1);
      }
    },
    // shouldBegin: (evt) => {
    //   if (updateIsEntranceNode) {
    //     return true;
    //   }
    //   return false;
    // },
    offsetX: 0,
    offsetY: 10,
    itemTypes: ["node"],
  });

  const graphInst = new G6.Graph({
    container: "topo",
    width,
    height: height,
    fitCenter: true,
    autoPaint: false,
    fitView: true,
    fitViewPadding: 25,
    maxZoom: 2,
    minZoom: 0.5,
    plugins: [nameTooltip, contextMenu],
    modes: {
      default: [
        {
          type: "zoom-canvas",
          minZoom: 0.5,
          maxZoom: 2,
        },
        {
          type: "drag-node",
          onlyChangeComboSize: true,
        },
        "drag-combo",
        "drag-canvas",
        {
          type: 'brush-select',
          trigger: 'ctrl',
          includeEdges: false,

        },
      ],
      // brush: [
      //   {
      //     type: 'brush-select',
      //     trigger: 'ctrl',
      //     includeEdges: false,

      //   },
      // ],
      showComponent: [],
    },
    layout: getLayoutConfig(),
    defaultNode: {
      type: props.nodeMode === 'circle' ? "circle-node" : 'custom-node',
    },
    groupByTypes: false,
    defaultEdge: {
      style: {
        endArrow: {
          path: G6.Arrow.triangle(5, 5, 0),
          fill: "#AAAAAA",
        },
        lineWidth: 1,
        stroke: "#AAAAAA",
      },
    },
    // preventOverlap:true,
    defaultCombo: {
      type: "custom-combo",
      size: [132, 0],
      padding: props.nodeMode === 'circle' ? [80, 80, 40, 40] : [54, 16, 16, 16],
    },
  });
  graphInst.data(data);
  graphInst.render();

  graph.value = graphInst;

  // * 因为layout设置的值总不太正常尝试修正combo的位置
  graphInst.on("afterrender", (evt) => {
    console.log(graphInst.getCombos(), 'combos');

    graphInst.fitCenter();
  });
  // * 增加交互
  for (let key in basicTopoInteractions) {
    const handler = basicTopoInteractions[key]
    graphInst.on(key, (evt) => handler(evt, graphInst))
  }
  graphInst.on('node:click', (evt) => {
    const { item } = evt;
    const targetName = evt.target.get("name");

    const model = item.getModel();
    if (model.id === "external") {
      return;
    }
    const inEdges = item.getInEdges();
    let srcNodes = [];
    if (model?.extraSrcNodes?.length) {
      srcNodes = model.extraSrcNodes;
    } else {
      // 需要过滤掉源端为external的节点(貌似不需要过滤掉了，这里不会有external点，先保留吧)
      srcNodes = map(inEdges, (edge) => edge.getSource().getModel()).filter(
        (node) => node.id !== "external"
      );
    }

    // todo 查看节点详情
    nodeToCheck.value = model;

    nodeDrawerState.value = {
      srcNodes: uniqBy(srcNodes, "id").filter(
        ({ type }) => type !== "custom-combo"
      ),
      visible: true,
      defaultKey: targetName?.indexOf("alarm-img") > -1 ? "alarm" : "net",
    }
    // this.setState({
    //   nodeInfo: model,
    //   srcNodes: uniqBy(srcNodes, "id").filter(
    //     ({ type }) => type !== "custom-combo"
    //   ),
    //   nodeDetailVisible: true,
    //   detailDefaultActiveKey:
    //     targetName?.indexOf("alarm-img") > -1 ? "alarm" : "net",
    // });
  })
  graphInst.on("edge:click", (evt) => {
    const { item } = evt;
    const source = item.getSource().getModel();
    const target = item.getTarget().getModel();
    if (source.collapsed || target.collapsed) {
      return;
    }
    if (source.id === "external") {
      return;
    }
    const model = item.getModel();
    edgeToCheck.value = model;
    edgeDrawerState.value.visible = true
    // this.setState({
    //     edgeInfo: model,
    //     edgeDetailVisible: true,
    // });
  });
  setEdgeLineWidth(graphInst);
  isAddExternal && addExternal(nodes, graphInst);
};
const closeNodeDetail = () => {
  nodeDrawerState.value.visible = false
}
const closeEdgeDetail = () => {
  edgeDrawerState.value.visible = false
}
console.log(props.dataFilter, 'filter');

watch([() => props.topoData, () => props.dataFilter.count, () => props.dataFilter.field, () => props.nodeMode], () => {
  setEdgeRequestTotalRange()
  console.log('draw, topoData change',);

  draw()
})
watch(() => props.isFullScreen, changeGraphSize)
onMounted(() => {
  setEdgeRequestTotalRange()
  draw()
  window.addEventListener('resize', changeGraphSize)
})
onUnmounted(() => {
  window.removeEventListener('resize', changeGraphSize)

})
defineExpose({
  graph,
  setEdgeLineWidth,
  // paint
})
</script>

<style scoped lang="scss"></style>
